<?php
/**
 * Klasse zur Datenbankcaching
 *
 * @author		Joachim Ruf
 * @copyright	Copyright (c) 2003-2013 Loresoft Software (http://www.loresoft.de)
 * @license		http://yapary.loresoft.de/lizenzbedingungen     New BSD License
 *
 * */


Class Yapary_Cache_DbCache
{
    private $oSql;

    /** @var Maximale Groesse eines zu Cachenden Datensatzes */
    const maxKbSize = 4294967296; //2^32

    /** @var Standard Lebensdauer eines zu Cachenden Datensatzes */
    private $expirationTime = 3600;

    /** @var Angabe ob Caching aktiviert oder deaktiviert ist */
    private $enable = true;

    /** @var Speichert die letzen DB-Results fuer get()-Aufrufe. Forma array[$id] = SqlRow */
    private $arrData = array();

    private $dbTableName = 'yapary_db_cache';


    function __construct (Yapary_Db_Sql_Connector $_oSql, $_expirationTime = 3600)
    {
		if (!$_oSql->database()->table($this->dbTableName)->exists()) {
            $e = new Exception();
            trigger_error('<pre>'.$e->getTraceAsString().'<br /><b>'.$this->dbTableName.':</b> No database installed</pre>', E_USER_WARNING);
		}

        $this->oSql = $_oSql;
        $this->setExpirationTime($_expirationTime);
    }


    /**
     * Setzt die Lebenszeit der zu Cachenden Daten
     * @param int $_expirationTime	: Zeitdauern in Sekunden
     * @return bool
     * */
    public function setExpirationTime ($_seconds = 3600)
    {
        if (is_numeric($_seconds)) {
            $this->expirationTime = $_seconds;
            return true;
        }

        return false;
    }


    /**
     * Aktiviert/Deaktiviert das Caching
     * Ist das Caching deaktiv, so wird immer in den zu Cachenden Teil gesprungen und dieser ausgefuerht
     * */
    public function enable ($_bool = true) {
        if (is_bool($_bool)) {
            $this->enable = $_bool;
        }
    }


    /**
     * Weisst den Browser an, in Ihm gecachte Daten zu loeschen.
     * Ohne Browser-Saeuberung kommt es sonst zu ungewollten Ausgaben
     * */
    private function clean ()
    {
//        ob_end_flush();
//        ob_flush();
//        flush();
//        ob_start();
        flush();
//        ob_flush();
    }


    /**
     * Prueft ob die Daten im Cache sind
     * @param mixed $id				: Einmalige Id des Cacheeintrages
     * @return string|NULL
     * */
    public function hit ($_id)
    {
        if ($this->enable == false) { // Wenn Caching deaktiviert ist, breche gleich ab. So wird immer in den zu Cachenen Part gesprungen
            return NULL;
        }

        $oDbResult = $this->oSql->request("SELECT Id, Cache
											FROM ".$this->dbTableName."
											WHERE
												Id=:id AND
												ExpirationTime>=:expirationTime
											",
											array(
												'id'             => $_id,
												'expirationTime' => time()
											));

		if ($oDbResult !== NULL && $oDbResult->getCount() == 1) {
			return base64_decode($oDbResult->getNext()->Cache);
		}

        return NULL;
    }


    /**
     * Caching starten
     * Nur fuer {echo|print|...} oder sonstige dirkte Ausgaben geeignet
     * @return void
     * */
    public function start ()
    {
        if ($this->enable == false) {
            return;
        }

        ob_start();
    }


    /**
     * gestartetes Caching stoppen
     * Nur fuer {echo|print|...} oder sonstige dirkte Ausgaben geeignet
     * @param mixed $id	: Einmalige Id, mit welcher die Daten abgespeichert werden sollen
     * @return bool|NULL
     * */
    public function stop ($_id, $_seconds = NULL)
    {
        if ($this->enable == false) {
            return NULL;
        }

        $content = ob_get_contents();
        ob_end_clean();
        return $this->add($_id, $content, $_seconds);
    }


    /**
     * Prueft ob die Daten im Cache sind
     * @param mixed $id				: Einmalige Id des Cacheeintrages
     * @return bool
     * */
    private function exists ($_id)
    {
		$oDbResult = $this->oSql->request("SELECT COUNT(Id) AS Counter
											FROM ".$this->dbTableName."
											WHERE Id=:id
											",
			                            	array('id' => $_id));

		if ($oDbResult !== NULL && $oDbResult->getNext()->Counter == 1) {
			return true;
		}

		return false;
    }


    /**
     * Fuegt einen neuen zu cachenden Datensatz hinzu
     * Geeignet um Variablen zu Cachen.
     * @param mixed $id				: Einmalige Id
     * @param string $content		: Zu cachender Content
     * @param int $expirationTime	: Dauer der Gueltigkeit des Caches in Sekunden
     * @return bool|NULL
     * */
    public function add ($_id, $_content, $_seconds = NULL)
    {
		if ($this->enable == false) {
			return NULL;
		}

		$seconds	= ($_seconds != NULL) ? $_seconds : $this->expirationTime;
		if ($this->exists($_id)) {
			return $this->update($_id, $_content);
        }

		$oDbResult = $this->oSql->request("INSERT INTO ".$this->dbTableName."
											(Id, Cache, ExpirationTime) VALUES (:id, :cache, :expirationTime)
											",
											array (
												'id'             => $_id,
												'cache'          => base64_encode($_content),
												'expirationTime' => time()+$seconds
											));
		return $oDbResult->isValid();
    }


    private function update ($_id, $_content, $_seconds = NULL) {
		$seconds	= ($_seconds != NULL) ? $_seconds : $this->expirationTime;

    	$oDbResult = $this->oSql->request("UPDATE ".$this->dbTableName."
											SET
												Cache=:cache,
												ExpirationTime=:expirationTime,
    											Requests=Requests+1
											WHERE
												Id=:id
											",
											array (
												'id'             => $_id,
												'cache'          => base64_encode($_content),
												'expirationTime' => time()+$seconds
											));

    	return $oDbResult->isValid();
    }

    /**
     * Liefert die gecachten Daten
     * @param mixed $id				: Einmalige Id
     * @return string|NULL
     * */
//     public function get ($id)
//     {
//         if ($this->enable == false) {
//             return '';
//         }

//         if (!isset($this->arrData[$id])) {
//             $oDbResult = $this->oSql->request("UPDATE ".$this->dbTableName."
//             									SET Requests=Requests+1
//             									");

//             $oDbResult = $this->oSql->request("SELECT *
// 			            					  FROM ".$this->dbTableName."
// 			            					  WHERE Id=:id AND ExpirationTime >=:expirationTime
//             								",
// 			                                array('id'             => $id,
// 			                                      'expirationTime' => time()));

//             if ($oDbResult->getCount() != 1) {
//                 return NULL;
//             }

//             $this->arrData[$id] = $oDbResult->getNext();
//         }

//         return base64_decode($this->arrData[$id]->Cache);
//     }


    /**
     * Loescht die gecachten Daten
     * @param mixed $id				: Einmalige Id der Daten die geloescht werden sollen
     * @return bool
     * */
    public function delete ($_id)
	{
		$this->clean();
		$oDbResult = $this->oSql->request("DELETE FROM ".$this->dbTableName."
											WHERE Id=:id",
											array(
												'id' => $id
											));

        return $oDbResult->isValid();
    }


    /**
     * @NOTICE Loescht den gesamten Cache
     * @return bool
     * */
    public function flush ()
    {
        $this->clean();
        $oDbResult = $this->oSql->request("DELETE FROM ".$this->dbTableName);
        return true;
    }


}


?>